#include "application.h"
#include "audio_codec.h"
#include "board.h"
#include "boards/common/wifi_board.h"
#include "settings.h"
#include "system_info.h"
#include <driver/gpio.h>
#include <esp_err.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs.h>
#include <nvs_flash.h>

#define TAG "main"

static uint8_t sg_application_start = 0;

void bMainBabyOSTask(void *arg) {
  bMsgInfo_t msg;
  QueueHandle_t bBos2MainQueue = NULL;
  bBos2MainQueue = xQueueCreate(10, sizeof(bMsgInfo_t));
  assert(bBos2MainQueue != NULL);
  bBabyOSRun(bBos2MainQueue);
  while (1) {
    if (xQueueReceive(bBos2MainQueue, &msg, portMAX_DELAY)) {
      printf("recv: %d\r\n", msg.type);
      switch (msg.type) {
      case MSG_TYPE_CFGNET_MODE: {
        Settings settings("wifi", true);
        if (msg.msg == NULL) {
          settings.SetString("bos_ssid", "BabyOS");
        } else {
          std::string _ssid(reinterpret_cast<char *>(msg.msg));
          settings.SetString("bos_ssid", _ssid);
        }
        auto board = static_cast<WifiBoard *>(&Board::GetInstance());
        if (board && board->GetBoardType() == "wifi") {
          board->ResetWifiConfiguration();
        }
      } break;
      case MSG_TYPE_VOICE_VOLUME: {
        int volume = (int)msg.msg;
        auto codec = Board::GetInstance().GetAudioCodec();
        codec->SetOutputVolume(static_cast<uint8_t>(volume));
      } break;
      case MSG_TYPE_DEVICE_INFO: {
        if (msg.msg) {
          char *pname = strstr((const char *)msg.msg, ",");
          if (pname != NULL) {
            pname[0] = '\0';
            pname += 1;
            Settings settings("mcu_dev", true);
            settings.SetString("product_ver", (const char *)msg.msg);
            settings.SetString("product_name", (const char *)pname);
            printf("product_ver:%s\r\nproduct_name:%s\r\n", msg.msg, pname);
            sg_application_start = 1;
          }
        }
      } break;
      case MSG_TYPE_REQ_OTA_DATA: {
        bMsgFwReqData_t *preqdata = (bMsgFwReqData_t *)msg.msg;
        int rlen = 0;
        uint8_t *fdata = (uint8_t *)malloc(preqdata->size);
        if (fdata) {
          rlen = Application::GetInstance().GetOtaRawData(
              preqdata->offset, preqdata->size, fdata);
          if (rlen >= 0) {
            bMsgFwData_t *pmsg_fdata =
                (bMsgFwData_t *)malloc(sizeof(bMsgFwData_t));
            if (pmsg_fdata) {
              pmsg_fdata->offset = preqdata->offset;
              pmsg_fdata->size = preqdata->size;
              pmsg_fdata->pdata = fdata;
              bBabyOSSendMsg(MSG_TYPE_NOTIFY_OTA_DATA, (uint8_t *)pmsg_fdata,
                             sizeof(bMsgFwData_t), free);
            } else {
              free(fdata);
              fdata = NULL;
            }
          }
        }
      } break;
      default:
        break;
      }
      if (msg.release && msg.msg) {
        msg.release(msg.msg);
      }
    }
  }
}

extern "C" void app_main(void) {

  uint8_t count = 0;
  xTaskCreate(bMainBabyOSTask, "main_bos", 4096, NULL, uxTaskPriorityGet(NULL),
              NULL);
  // Initialize the default event loop
  ESP_ERROR_CHECK(esp_event_loop_create_default());

  // Initialize NVS flash for WiFi configuration
  esp_err_t ret = nvs_flash_init();
  if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
      ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_LOGW(TAG, "Erasing NVS flash to fix corruption");
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
  }
  ESP_ERROR_CHECK(ret);

  while (sg_application_start == 0) {
    vTaskDelay(pdMS_TO_TICKS(1000));
    count += 1;
    bBabyOSSendMsg(MSG_TYPE_DEVICE_INFO, NULL, 0, NULL);
    if (count > 10) {
      break;
    }
  }

  // Launch the application
  Application::GetInstance().Start();
  // The main thread will exit and release the stack memory
}
